﻿#region using

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

#endregion

namespace JupiterLibrary
{
    /// <summary>
    /// Mantiene la declaración de los vértices. 
    /// Nunca se debería acceder a esta clase, puesto que la utiliza la clase <see cref="VB"/> para obtener la declaración de los vértices
    /// </summary>
    internal class VDecl
    {
        #region Fields

        /// <summary>The XNA vertexDeclaration.</summary>
        VertexDeclaration vertexDeclaration;

        #endregion

        #region Properties

        public VertexElement[] VertexElements { get { return vertexDeclaration.GetVertexElements(); } }

        public VertexDeclaration VertexDeclaration { get { return vertexDeclaration; } }

        #endregion

        #region Constructor

        public VDecl(VertexElement[] vertexElements)
        {
            vertexDeclaration = new VertexDeclaration(vertexElements);
        }

        #endregion

        #region Dispose

        public void Dispose()
        {
            if (vertexDeclaration != null)
            {
                vertexDeclaration.Dispose();

                vertexDeclaration = null;
            }
        }

        #endregion

    }

    /// <summary>
    /// Gestiona la colección de <see cref="VDecl"/>. Hay que utilizar el método GetDeclaration de esta clase para poder acceder a los VDecl
    /// Nunca se debería acceder a esta clase, puesto que la utiliza la clase <see cref="VB"/> para obtener la declaración de los vértices
    /// </summary>
    internal class VDecls
    {

        public List<VDecl> List;

        public VDecls()
        {
            List = new List<VDecl>();
        }

        /// <summary>
        /// Dispose and removes the list elements
        /// </summary>
        public void Dispose()
        {
            foreach (VDecl decl in List)
            {
                decl.Dispose();
            }

            List.Clear();
        }

        VertexElement[] Build(VB vb)
        {
            int vertexCount = 1;  // We always need the Position

            // Check the elements

            if (vb.Color != null) vertexCount++;

            if (vb.Normal != null) vertexCount++;

            if (vb.TextureCoord != null) vertexCount++;

            VertexElement[] vertexElements = new VertexElement[vertexCount];

            int offset=0;

            vertexCount = 0;

            vertexElements[vertexCount++] = new VertexElement(offset, VertexElementFormat.Vector3, VertexElementUsage.Position, 0);

            offset += 3 * sizeof(float);

            if (vb.Color!=null)
            {
                vertexElements[vertexCount++] = new VertexElement(offset, VertexElementFormat.Color, VertexElementUsage.Color, 0);

                // Color is defined as ARGB = 32 bits
                offset += 1 * sizeof(float);
            }

            if (vb.Normal != null)
            {
                vertexElements[vertexCount++] = new VertexElement(offset, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0);

                // Add Vector3
                offset += 3 * sizeof(float);
            }

            if (vb.TextureCoord != null)
            {
                vertexElements[vertexCount++] = new VertexElement(offset, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0);

                // Add Vector2.X and Vector2.Y
                offset += 2 * sizeof(float);
            }

            return vertexElements;

        }

        /// <summary>
        /// Obtanis a <see cref="VDecl"/> that matches the vertexubuffer declaration
        /// </summary>
        /// <param name="vertexBuffer"></param>
        /// <returns></returns>
        public VDecl GetDeclaration(VB vertexBuffer)
        {
            // Check if exists

            VertexElement[] vertexElements = Build(vertexBuffer);

            foreach (VDecl declaration in List)
            {
                // First we can compare size
                if (declaration.VertexElements.Length == vertexElements.Length)
                {
                    // Now, we must compare element by element

                    bool same = true;

                    for (int i = 0; i < declaration.VertexElements.Length; i++)
                    {
                        if (vertexElements[i].Offset != declaration.VertexElements[i].Offset) { same = false; break; }
                        if (vertexElements[i].VertexElementFormat != declaration.VertexElements[i].VertexElementFormat) { same = false; break; }
                        if (vertexElements[i].VertexElementUsage != declaration.VertexElements[i].VertexElementUsage) { same = false; break; }
                        if (vertexElements[i].UsageIndex != declaration.VertexElements[i].UsageIndex) { same = false; break; }
                    }

                    // If it's the same, we do not need to return a new object, so we return the declaration that match the needed
                    if (same) return declaration;
                }
            }

            // No declaration found that matches the user need, so we need to build a new one

            VDecl newDeclaration = new VDecl(vertexElements);

            List.Add(newDeclaration);

            return newDeclaration;
        }
    }
}
